<?php

declare ( strict_types = 1 )
;

namespace Initiating\TransactionResources;

use Initiating\Client\TxClientManage;
use Communication\TxSocketData;
use Trance\TransactionGroup;
use Trance\Transaction;
use Compensate\Invocation;
use Initiating\Contract\ITxTransaction;
use Communication\Contract\ITxDatabase;
use Swoole\Coroutine;
use UntilDistributed\Constant;
use UntilDistributed\Util;
use UntilDistributed\Log;

/**
 * 事务资源句柄
 */
abstract class TransactionHandler implements ITxTransaction {
	/**
	 * 事务组
	 *
	 * @var TransactionGroup
	 */
	protected $transGroup;
	
	/**
	 * 事务
	 *
	 * @var Transaction
	 */
	protected $transaction;
	
	/**
	 *
	 * @var string
	 */
	protected $groupId = '';
	
	/**
	 * 客户端
	 *
	 * @var TxClientManage
	 */
	protected $swClient;
	
	
	private const WAIT_FOR_RECEIVE_DATA = [
			Constant::SOCKET_ACTION_PRECOMMIT => 'socketActionPrecommit',
			Constant::SOCKET_ACTION_ROLLBACK => 'socketActionRollback',
			Constant::SOCKET_ACTION_COMMIT => 'socketActionCommit'
			
	];
	
	/**
	 *
	 * @var ITxDatabase
	 */
	protected $transDb;
	
	
	function __construct() {
	}
	public function getTxClientManage(): TxClientManage {
		return $this->swClient;
	}
	
	public function initTransDb(ITxDatabase $db = null) {
		
		$this->transDb = $db;
		
	}
	/**
	 * 初始化 Client
	 */
	protected function initSwooleClient(): void {
		$this->swClient = new TxClientManage ();
	}
	
	/**
	 * 初始化事务参与者
	 * @param ITxDatabase $db
	 * @param string $groupId
	 */
	public function initTrans(ITxDatabase $db = null, string $groupId =''): void {
		
		$this->initTransDb($db);
		
		$this->initTxGroup($db, $groupId);
		
		
	}
	
	/**
	 * 初始化事务组
	 * @param ITxDatabase $db
	 * @param string $groupId
	 */
	public function initTxGroup(ITxDatabase $db = null, string $groupId =''): void {
		
		// 创建事务组信息
		$transGroup = new TransactionGroup ( $groupId );
		$propagation = $db ? Constant::$propagation_have : Constant::$propagation_naver;
		$role = $groupId ? Constant::$txgroup_role_actor : Constant::$txgroup_role_starter;
		
		// 添加发起者
		$transaction = new Transaction ();
		if ($role == Constant::$txgroup_role_starter) {
			$transaction->transId = Util::createStarterId ( $transGroup->groupId );
		} else {
			$transaction->transId = Util::createActorId ( $transGroup->groupId );
		}
		$transaction->role = $role;
		$transaction->status = Constant::$tx_status_begin;
		$transaction->groupId = $transGroup->groupId;
		$transaction->waitMaxTime = 3;
		$transaction->createTime = microtime ( true );
		$transaction->propagation = $propagation;
		
		// 设置事务执行类和事务补偿类（反射执行）
		$invocation = new Invocation ();
		$transaction->invocation = $invocation;
		
		$this->transGroup = $transGroup;
		$this->transaction = $transaction;
	}
	/**
	 * 设置超时时间
	 * @param int $timeout
	 */
	public function setTimeOut(int $timeout = 0): void {
		if ($timeout) {
			$this->transaction->waitMaxTime = $timeout;
			$this->transGroup->waitTime = $timeout;
		}
	}
	
	/**
	 * 发起者注册事务组
	 *
	 * @return ? TransactionGroup
	 */
	public function addTxGroup(): ? TransactionGroup {
		// 封装请求数据
		$socketData = new TxSocketData ();
		$socketData->transGroup = $this->transGroup;
		$socketData->action = Constant::SOCKET_ACTION_STARTTRANS;
		$socketData->transaction = $this->transaction;
		
		$config = array (
				'timeout' => $this->transaction->waitMaxTime
		);
		$this->swClient->connect ( $config );
		$this->swClient->sendMsg ( $socketData );
		
		$ret = $this->swClient->recv ();
		
		
		
		if ($ret->result == Constant::$tx_complete_ok) {
			return $this->transGroup;
		}
		
		return null;
	}
	
	/**
	 * 注册事务参与者
	 *
	 * @return [type] [description]
	 */
	protected function registTransaction(): ?Transaction {
		// 封装socket数据
		$socketData = new TxSocketData ();
		$socketData->transGroup = $this->transGroup;
		$socketData->action = Constant::SOCKET_ACTION_REGTXACTOR;
		$socketData->transaction = $this->transaction;
		
		$config = array (
				'timeout' => $this->transaction->waitMaxTime
		);
		$this->swClient->connect ( $config );
		
		$this->swClient->sendMsg ( $socketData );
		
		$ret = $this->swClient->recv ();
		
		if ($ret->result == Constant::$tx_complete_ok) {
			return $this->transaction;
		}
		
		return null;
	}
	
	/**
	 * 发起者发起precommit
	 *
	 * @return [type] [description]
	 */
	protected function preCommit(): bool {
		// 发送
		$socketData = new TxSocketData ();
		$socketData->transGroup = $this->transGroup;
		$socketData->action = Constant::SOCKET_ACTION_PRECOMMIT;
		$socketData->transaction = $this->transaction;
		
		$ret = $this->swClient->sendMsg ( $socketData );
		
		if (! $ret) {
			// rollBack
			Log::getInstance ()->error ( "starter: send precommit failure , will rollBack" );
		} else {
			$result = $this->swClient->recv ();
			// dump($result);
			if (! empty ( $result ) && $result->result == Constant::$tx_complete_ok) {
				
				$this->transaction->status = Constant::$tx_status_precommit;
				return true;
			} else {
				// rollBack
				Log::getInstance ()->error ( "starter: precommit recv  response failure" );
			}
		}
		
		if ($this->transaction->role === Constant::$txgroup_role_actor) {
			$this->localRollback();
		}
		
		// 标记事务状态
		$this->transaction->status = Constant::$tx_status_rollback;
		
		return false;
	}
	
	/**
	 * 发起者发起docommit
	 *
	 * @return [type] [description]
	 */
	protected function doCommit(): bool {
		// 发送指令
		$socketData = new TxSocketData ();
		$socketData->transGroup = $this->transGroup;
		$socketData->transaction = $this->transaction;
		$socketData->action = Constant::SOCKET_ACTION_COMMIT;
		
		$ret = $this->swClient->sendMsg ( $socketData );
		if (! $ret) {
			// 发送commit失败，位链接上
			Log::getInstance ()->error ( "starter: send commit msg failure, local transaction will commit" );
			
			return false;
		} else {
			// 接收commit回复
			$result = $this->swClient->recv ();
			if (! $result) {
				// 接收超时或失败
				Log::getInstance ()->error ( "starter: wait commit response timeout" );
				
				return false;
			}
			if ($result->result == Constant::$tx_complete_fail) {
				Log::getInstance ()->error ( "starter: txmansge preCommit failure" );
				
				return false;
			}
		}
		
		if ($this->transaction->role === Constant::$txgroup_role_actor) {
			// 提交本地事务
			$this->transDb->commit();
		}
		
		$this->transaction->status = Constant::$tx_status_commit;
		
		return true;
	}
	
	/**
	 * 本地事务回滚
	 *
	 * @return [type] [description]
	 */
	protected function localRollback(): void {
		Log::getInstance ()->error ( "local rollBack" );
		// 本地事务回滚
		
		$this->transDb->rollBack();
		
		$this->transaction->status = Constant::$tx_status_rollback;
	}
	
	/**
	 * 发起回滚
	 *
	 * @return [type] [description]
	 */
	protected function doRollback(): bool {
		
		
		
		// 		if ($this->transaction->role == Constant::$txgroup_role_starter) {
		// 发送
		$socketData = new TxSocketData ();
		$socketData->transGroup = $this->transGroup;
		$socketData->action = Constant::SOCKET_ACTION_ROLLBACK;
		$socketData->transaction = $this->transaction;
		
		$this->swClient->connect ( );
		
		
		$ret = $this->swClient->sendMsg ( $socketData );
		
		
		if (! $ret) {
			// rollBack
			log::getInstance ()->error ( "starter: send rollBack failure , local rollBack" );
			
			return false;
		}
		// 		} else {
		// 参与者处理失败
		$this->transaction->status = Constant::$tx_status_rollback;
		// 		}
		
		$this->localRollback ();
		
		// 		if ($this->swClient->getSwooleClient()->isConnected()) {
		// 			$this->swClient->getSwooleClient()->close();
		// 		}
		
		
		return true;
	}
	
	/**
	 * 参与者等待调用
	 *
	 * @return
	 */
	protected function waitForReceiveData(): int {
		
		return Coroutine::create ( function (TransactionHandler $handler) {
			
			Coroutine::sleep(0.05);
			try {
				$socketData = $handler->getTxClientManage()->recv ();
				
				if (! $socketData) {
					return false;
				}
				
				if (! isset ( TransactionHandler::WAIT_FOR_RECEIVE_DATA [$socketData->action] )) {
					return true;
				}
				
				$method = TransactionHandler::WAIT_FOR_RECEIVE_DATA [$socketData->action];
				
				return $handler->$method ( $socketData );
				
			} catch (\Exception $e) {
				vdump($e->getFile().'；'.$e->getLine().'；'.$e->getMessage());
			} finally {
			}
			
		}, $this );
	}
	
	/**
	 * 执行预提交结果发送
	 * @param TxSocketData $socketData
	 * @return bool
	 */
	private function socketActionPrecommit(TxSocketData $socketData): bool
	{
		// 判断事务状态
		if ($this->transaction->status != Constant::$tx_status_cancommit) {
			$socketData->result = Constant::$tx_complete_ok;
			$socketData->action = Constant::SOCKET_RESULT_PRE_COMMIT;
			$this->swClient->sendMsg ( $socketData );
			
			Log::getInstance ()->info ( "actor: preCommit response failure" );
			return false;
		}
		
		Log::getInstance ()->info ( "actor: preCommit response ok" );
		// 设置事务状态
		$this->transaction->status = Constant::$tx_status_precommit;
		
		$socketData->result = Constant::$tx_complete_ok;
		$socketData->action = Constant::SOCKET_RESULT_PRE_COMMIT;
		$this->swClient->sendMsg ( $socketData );
		
		$command_commit = $this->swClient->recv ();
		
		if (! $command_commit || $command_commit->action == Constant::SOCKET_ACTION_COMMIT) {
			
			if ($this->transaction->status == Constant::$tx_status_precommit) {
				$this->transDb->commit ();
				$this->transaction->status = Constant::$tx_status_commit;
			}
		} else if ($command_commit->action == Constant::SOCKET_ACTION_ROLLBACK) {
			$this->localRollback ();
			return false;
		} else {
			// 超时提交
			$this->transDb->commit();
			$this->transaction->status = Constant::$tx_status_commit;
		}
		return true;
	}
	
	/**
	 * 提交事务
	 * @param TxSocketData $socketData
	 * @return bool
	 */
	private function socketActionCommit(TxSocketData $socketData): bool
	{
		try {
			$this->transDb->commit();
		} catch (\Exception $e) {
			Log::getInstance()->error($e->getMessage());
			return false;
		}
		return true;
	}
	
	/**
	 * 执行回滚操作
	 * @param TxSocketData $socketData
	 * @return bool
	 */
	private function socketActionRollback(TxSocketData $socketData): bool
	{
		try {
			$this->localRollback();
		} catch (\Exception $e) {
			Log::getInstance()->error($e->getMessage());
			return false;
		}
		return true;
	}
}